/** @brief File containing the implementation of the basic hexagon grid.
 *
 * This file contains the implementation of a grid visualisation where
 * the cells are hexagons.
 *
 * @file HexagonRepresentation.cpp
 * @author Johan "Solo" Luisier
 * @date 2013/03/26
 */
#include "HexagonRepresentation.hpp"

#include "Core/GridBase.hpp"

#include <QtCore/QDebug>
#include <QtCore/qmath.h>
#include <QtCore/QPoint>
#include <QtCore/QPointF>

#include <QtSvg/QGraphicsSvgItem>
#include <QtSvg/QSvgRenderer>

const qreal HexagonRepresentation::TileWidth( 10. );
const qreal HexagonRepresentation::TileHeight( 11.5 );

const qreal HexagonRepresentation::HeightFraction( 1. / ( 2. * qSqrt( 3. ) ) );

HexagonRepresentation::HexagonRepresentation( GridBase *grid, QObject *parent )
    : RepresentationBase( grid, parent )
    , TopLeftPosition( 0., 0. )
    , BottomRightPosition( 0., 0. )
{
    setupTiles();
}

HexagonRepresentation::~HexagonRepresentation()
{}

void HexagonRepresentation::retrieveCell( const QPointF& pt )
{
    /**
     * The used method is the following: first "virtual" rectangular tiles are
     * used to roughly locate the touched area. Those virtual tiles are chosen
     * to overlap most of a hexagonal tile and a tiny part of the two tiles
     * located below (see here below, the figure "Mechanism of tile
     * identification"). The horizontal position of the virtual tile depends on
     * the row number, which is deduced from the vertical position.
     *
     * @image latex HexagonIdentify-1.eps "Mechanism of tile identification" width=8cm
     * @image html HexagonIdentify-1.png "Mechanism of tile identification"
     *
     * From the coordinates of the top left vertex of the virtual tile the
     * coordinates of the "main" tile can be deduced, main tile means the
     * hexagon which overlapping most of the virtual tile. If the top left
     * corner of the virtual tile coordinates are @f$ ( i \cdot {width}, j
     * \cdot {height} ) @f$, the main tile is tile @f$ ( i, j ) @f$, and
     * the two tiles below it can be deduced from the parity of @f$ j @f$ (see
     * picture "Virtual tile to real tile matching").
     *
     * @image html HexagonIdentify-2.png "Virtual tile to real tile matching"
     *
     * The exact pressed tile is determined the following way: if the @f$ y @f$
     * coordinate of the pressed point lies in the part of the virtual tile
     * where only one tile exists, the case is settled. If the press happens to
     * lies in the bottom part of the virtual tile, then trigonometry is used:
     * the angle between the @f$ \vec{e} @f$ reference vector and the press
     * point @f$ \vec{r} @f$ is computed and used to determined which tile was
     * clicked. In the example on the next figure, the @f$ ( i + 1, k + 1 ) @f$
     * tile was pressed.
     *
     * @image html HexagonIdentify-3.png "Exact determination of the clicked tile, disambiguation"
     */

    int i( 0 ), j( qFloor( pt.y() / ( tileHeight() * ( 1. - HeightFraction ) ) ) );
    QPointF squareTopLeft;

    squareTopLeft.setY( j * tileHeight() * ( 1. - HeightFraction ) );

    if ( j % 2 == 0 )
    {
        i = qFloor( pt.x() / tileWidth() );
        squareTopLeft.setX( i * tileWidth() );
    }
    else
    {
        i = qFloor( pt.x() / tileWidth() + 0.5 );
        squareTopLeft.setX( ( qreal( i ) - 0.5 ) * tileWidth() );
    }

    QPointF relative( pt );

    relative -= squareTopLeft;
    relative.rx() /= tileWidth();
    relative.ry() /= tileHeight() * ( 1. - HeightFraction );

    if ( relative.y() <= 1. - 2. * HeightFraction )
    {
        qDebug() << "Touched cell" << i << j;

        emit( touchedCell( QPoint( i, j ) ) );
        return;
    }

    relative -= QPointF( 0.5, 1. );

    relative /= qSqrt( qPow( relative.x(), 2. ) + qPow( relative.y(), 2. ) );


    if ( relative.x() < - qSqrt( 3. ) / 2. )
    {
        if ( j % 2 == 0 )
            j++;
        else
        {
            i--;
            j++;
        }
    }
    else if ( relative.x() > qSqrt( 3. ) / 2. )
    {
        if ( j % 2 == 0 )
        {
            i++;
            j++;
        }
        else
            j++;
    }

    qDebug() << "Touched cell" << i << j;

    emit( touchedCell( QPoint( i, j ) ) );
}

void HexagonRepresentation::setupTiles()
{
    /**
     * Instead of using a complicated logic to know which Cell instance have to
     * be connected to which (question is relevant for the edges only), it is
     * made use of the logic put in addNeighbour: if the added neighbour
     * doesn't exist, (i.e. the pointer is null), then no neighbour is added.
     *
     * The computed @f$ y @f$ position of the hexagon must take into account
     * the fact that it overlaps with the one from the previous row.
     */

    quint32 i, j;

    QSvgRenderer* renderer = new QSvgRenderer( QString( ":/tiles" ) );

    qreal offset( HeightFraction );

    for ( j = 0u; j < GridModel -> height(); j++ )
    {
        for ( i = 0u; i < GridModel -> width(); i++ )
        {
            QGraphicsSvgItem* tile = new QGraphicsSvgItem();
            tile -> setSharedRenderer( renderer );
            tile -> setElementId( QString( "BlueHexagon" ) );
            if ( j % 2u == 0u )
                tile -> setPos( i * tileWidth(),
                                ( qreal( j ) - offset ) * tileHeight() );
            else
                tile -> setPos( ( qreal( i ) - 0.5 ) * tileWidth(),
                                ( qreal( j ) - offset ) * tileHeight() );
            tile -> hide();
            addItem( tile );
            CellTiles.append( tile );
        }
        offset += HeightFraction;
    }

    /**
     * The position of the bottom right corner depends on the parity of the
     * number of lines and columns.
     */
    BottomRightPosition.ry() += tileHeight() * ( qreal( GridModel -> height() ) *
                                                 ( 1. - HeightFraction ) - HeightFraction );

    if ( GridModel -> width() % 2u == 0u )
        BottomRightPosition.rx() += tileWidth() * ( qreal( GridModel -> width() ) - 0.5 );
    else
        BottomRightPosition.rx() += tileWidth() * GridModel -> width();
}

